﻿using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
using Microsoft.Win32;
using SHA3.Net;
using K4os.Hash.xxHash;
using Blake3;
using Force.Crc32;
using Org.BouncyCastle.Crypto.Digests;

namespace md5app;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private enum AlgorithmType
    {
        MD5,
        SHA1,
        SHA3_256,
        SHA3_384,
        SHA3_512,
        SHA256,
        SHA512,
        XXHASH64,
        BLAKE2B,
        BLAKE2S,
        BLAKE3,
        CRC32
    }

    private AlgorithmType _selectedAlgorithm = AlgorithmType.MD5;

    public MainWindow()
    {
        InitializeComponent();
        InitializeAlgorithmComboBox();
    }

    private void InitializeAlgorithmComboBox()
    {
        var algorithms = new List<string>
        {
            "MD5",
            "SHA-1",
            "SHA-3 (256)",
            "SHA-3 (384)",
            "SHA-3 (512)",
            "SHA256",
            "SHA512",
            "xxHash64",
            "Blake2B",
            "Blake2S",
            "Blake3",
            "CRC32"
        };

        AlgorithmComboBox.ItemsSource = algorithms;
        AlgorithmComboBox.SelectedItem = "MD5";
    }

    private void SelectFileButton_Click(object sender, RoutedEventArgs e)
    {
        var openFileDialog = new OpenFileDialog
        {
            Title = "选择要计算哈希值的文件",
            Filter = "所有文件 (*.*)|*.*"
        };

        if (openFileDialog.ShowDialog() == true)
        {
            string filePath = openFileDialog.FileName;
            FilePathTextBox.Text = filePath;
            StatusTextBlock.Text = "正在计算哈希值...";
            StatusTextBlock.Foreground = System.Windows.Media.Brushes.Blue;
            Md5TextBox.Text = "";
            CopyButton.IsEnabled = false;

            // 异步计算MD5值，避免界面冻结
            Task.Run(() =>
            {
                try
                {
                    string hash = CalculateHash(filePath, _selectedAlgorithm);
                    
                    Dispatcher.Invoke(() =>
                    {
                        Md5TextBox.Text = ApplyCasing(hash);
                        CopyButton.IsEnabled = true;
                        StatusTextBlock.Text = "哈希值计算完成";
                        StatusTextBlock.Foreground = System.Windows.Media.Brushes.Green;
                    });
                }
                catch (Exception ex)
                {
                    Dispatcher.Invoke(() =>
                    {
                        Md5TextBox.Text = $"错误: {ex.Message}";
                        StatusTextBlock.Text = "计算失败";
                        StatusTextBlock.Foreground = System.Windows.Media.Brushes.Red;
                    });
                }
            });
        }
    }

    private static string ToHexString(byte[] bytes)
    {
        var sb = new StringBuilder(bytes.Length * 2);
        foreach (var b in bytes) sb.Append(b.ToString("x2"));
        return sb.ToString();
    }

    private string CalculateHash(string filePath, AlgorithmType algorithm)
    {
        switch (algorithm)
        {
            case AlgorithmType.MD5:
            {
                using var md5 = MD5.Create();
                using var stream = File.OpenRead(filePath);
                return ToHexString(md5.ComputeHash(stream));
            }
            case AlgorithmType.SHA1:
            {
                using var sha1 = SHA1.Create();
                using var stream = File.OpenRead(filePath);
                return ToHexString(sha1.ComputeHash(stream));
            }
            case AlgorithmType.SHA256:
            {
                using var sha256 = SHA256.Create();
                using var stream = File.OpenRead(filePath);
                return ToHexString(sha256.ComputeHash(stream));
            }
            case AlgorithmType.SHA512:
            {
                using var sha512 = SHA512.Create();
                using var stream = File.OpenRead(filePath);
                return ToHexString(sha512.ComputeHash(stream));
            }
            case AlgorithmType.SHA3_256:
            {
                // 使用 BouncyCastle 流式计算 SHA3-256
                var digest = new Sha3Digest(256);
                var buffer = new byte[8192];
                using var fs = File.OpenRead(filePath);
                int read;
                while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    digest.BlockUpdate(buffer, 0, read);
                }
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.SHA3_384:
            {
                var digest = new Sha3Digest(384);
                var buffer = new byte[8192];
                using var fs = File.OpenRead(filePath);
                int read;
                while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    digest.BlockUpdate(buffer, 0, read);
                }
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.SHA3_512:
            {
                var digest = new Sha3Digest(512);
                var buffer = new byte[8192];
                using var fs = File.OpenRead(filePath);
                int read;
                while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    digest.BlockUpdate(buffer, 0, read);
                }
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.XXHASH64:
            {
                // xxHash64 返回 ulong，转十六进制（16位，小写）
                byte[] data = File.ReadAllBytes(filePath);
                ulong v = XXH64.DigestOf(data);
                return v.ToString("x16");
            }
            case AlgorithmType.BLAKE2B:
            {
                var digest = new Blake2bDigest(256);
                var buffer = new byte[8192];
                using var fs = File.OpenRead(filePath);
                int read;
                while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    digest.BlockUpdate(buffer, 0, read);
                }
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.BLAKE2S:
            {
                var digest = new Org.BouncyCastle.Crypto.Digests.Blake2sDigest(256);
                var buffer = new byte[8192];
                using var fs = File.OpenRead(filePath);
                int read;
                while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    digest.BlockUpdate(buffer, 0, read);
                }
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.BLAKE3:
            {
                byte[] data = File.ReadAllBytes(filePath);
                var hash = Blake3.Hasher.Hash(data); // 32字节
                // Blake3.Hash 提供十六进制字符串
                return hash.ToString();
            }
            case AlgorithmType.CRC32:
            {
                byte[] data = File.ReadAllBytes(filePath);
                uint crc = Crc32Algorithm.Compute(data);
                return crc.ToString("x8");
            }
            default:
                throw new NotSupportedException("不支持的算法");
        }
    }

    private string CalculateHashFromText(string text, AlgorithmType algorithm)
    {
        byte[] data = Encoding.UTF8.GetBytes(text);
        
        switch (algorithm)
        {
            case AlgorithmType.MD5:
            {
                using var md5 = MD5.Create();
                return ToHexString(md5.ComputeHash(data));
            }
            case AlgorithmType.SHA1:
            {
                using var sha1 = SHA1.Create();
                return ToHexString(sha1.ComputeHash(data));
            }
            case AlgorithmType.SHA256:
            {
                using var sha256 = SHA256.Create();
                return ToHexString(sha256.ComputeHash(data));
            }
            case AlgorithmType.SHA512:
            {
                using var sha512 = SHA512.Create();
                return ToHexString(sha512.ComputeHash(data));
            }
            case AlgorithmType.SHA3_256:
            {
                var digest = new Sha3Digest(256);
                digest.BlockUpdate(data, 0, data.Length);
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.SHA3_384:
            {
                var digest = new Sha3Digest(384);
                digest.BlockUpdate(data, 0, data.Length);
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.SHA3_512:
            {
                var digest = new Sha3Digest(512);
                digest.BlockUpdate(data, 0, data.Length);
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.XXHASH64:
            {
                ulong v = XXH64.DigestOf(data);
                return v.ToString("x16");
            }
            case AlgorithmType.BLAKE2B:
            {
                var digest = new Blake2bDigest(256);
                digest.BlockUpdate(data, 0, data.Length);
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.BLAKE2S:
            {
                var digest = new Org.BouncyCastle.Crypto.Digests.Blake2sDigest(256);
                digest.BlockUpdate(data, 0, data.Length);
                var result = new byte[digest.GetDigestSize()];
                digest.DoFinal(result, 0);
                return ToHexString(result);
            }
            case AlgorithmType.BLAKE3:
            {
                var hash = Blake3.Hasher.Hash(data);
                return hash.ToString();
            }
            case AlgorithmType.CRC32:
            {
                uint crc = Crc32Algorithm.Compute(data);
                return crc.ToString("x8");
            }
            default:
                throw new NotSupportedException("不支持的算法");
        }
    }

    private string ApplyCasing(string hex)
    {
        if (string.IsNullOrEmpty(hex)) return hex;
        return UppercaseCheckBox != null && UppercaseCheckBox.IsChecked == true
            ? hex.ToUpperInvariant()
            : hex.ToLowerInvariant();
    }

    private void ClearTextButton_Click(object sender, RoutedEventArgs e)
    {
        TextInputTextBox.Text = "";
        TextInputTextBox.Focus();
    }

    private void CalculateTextButton_Click(object sender, RoutedEventArgs e)
    {
        string text = TextInputTextBox.Text;
        if (string.IsNullOrEmpty(text))
        {
            StatusTextBlock.Text = "请输入要计算的文本";
            StatusTextBlock.Foreground = System.Windows.Media.Brushes.Orange;
            return;
        }

        StatusTextBlock.Text = "正在计算文本哈希值...";
        StatusTextBlock.Foreground = System.Windows.Media.Brushes.Blue;
        Md5TextBox.Text = "";
        CopyButton.IsEnabled = false;

        // 异步计算，避免界面冻结
        Task.Run(() =>
        {
            try
            {
                string hash = CalculateHashFromText(text, _selectedAlgorithm);
                
                Dispatcher.Invoke(() =>
                {
                    Md5TextBox.Text = ApplyCasing(hash);
                    CopyButton.IsEnabled = true;
                    StatusTextBlock.Text = "文本哈希值计算完成";
                    StatusTextBlock.Foreground = System.Windows.Media.Brushes.Green;
                });
            }
            catch (Exception ex)
            {
                Dispatcher.Invoke(() =>
                {
                    Md5TextBox.Text = $"错误: {ex.Message}";
                    StatusTextBlock.Text = "计算失败";
                    StatusTextBlock.Foreground = System.Windows.Media.Brushes.Red;
                });
            }
        });
    }

    private void CopyButton_Click(object sender, RoutedEventArgs e)
    {
        if (!string.IsNullOrEmpty(Md5TextBox.Text))
        {
            Clipboard.SetText(Md5TextBox.Text);
            StatusTextBlock.Text = "哈希值已复制到剪贴板";
            StatusTextBlock.Foreground = System.Windows.Media.Brushes.Green;
        }
    }

    private void AlgorithmComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        if (AlgorithmComboBox.SelectedItem is string selected)
        {
            _selectedAlgorithm = selected switch
            {
                "MD5" => AlgorithmType.MD5,
                "SHA-1" => AlgorithmType.SHA1,
                "SHA-3 (256)" => AlgorithmType.SHA3_256,
                "SHA-3 (384)" => AlgorithmType.SHA3_384,
                "SHA-3 (512)" => AlgorithmType.SHA3_512,
                "SHA256" => AlgorithmType.SHA256,
                "SHA512" => AlgorithmType.SHA512,
                "xxHash64" => AlgorithmType.XXHASH64,
                "Blake2B" => AlgorithmType.BLAKE2B,
                "Blake2S" => AlgorithmType.BLAKE2S,
                "Blake3" => AlgorithmType.BLAKE3,
                "CRC32" => AlgorithmType.CRC32,
                _ => _selectedAlgorithm
            };
        }
    }

    private void UppercaseCheckBox_Changed(object sender, RoutedEventArgs e)
    {
        if (!string.IsNullOrEmpty(Md5TextBox.Text))
        {
            Md5TextBox.Text = ApplyCasing(Md5TextBox.Text);
        }
    }
}